var zrUtil = require("zrender/lib/core/util");

var _number = require("../../util/number");

var parsePercent = _number.parsePercent;

var _graphic = require("../../util/graphic");

var subPixelOptimize = _graphic.subPixelOptimize;
var retrieve2 = zrUtil.retrieve2;

function _default(ecModel) {
  ecModel.eachSeriesByType('candlestick', function (seriesModel) {
    var coordSys = seriesModel.coordinateSystem;
    var data = seriesModel.getData();
    var candleWidth = calculateCandleWidth(seriesModel, data);
    var chartLayout = seriesModel.get('layout');
    var variableDim = chartLayout === 'horizontal' ? 0 : 1;
    var constDim = 1 - variableDim;
    var coordDims = ['x', 'y'];
    var vDims = [];
    var cDim;
    zrUtil.each(data.dimensions, function (dimName) {
      var dimInfo = data.getDimensionInfo(dimName);
      var coordDim = dimInfo.coordDim;

      if (coordDim === coordDims[constDim]) {
        vDims.push(dimName);
      } else if (coordDim === coordDims[variableDim]) {
        cDim = dimName;
      }
    });

    if (cDim == null || vDims.length < 4) {
      return;
    }

    var dataIndex = 0;
    data.each([cDim].concat(vDims), function () {
      var args = arguments;
      var axisDimVal = args[0];
      var idx = args[vDims.length + 1];
      var openVal = args[1];
      var closeVal = args[2];
      var lowestVal = args[3];
      var highestVal = args[4];
      var ocLow = Math.min(openVal, closeVal);
      var ocHigh = Math.max(openVal, closeVal);
      var ocLowPoint = getPoint(ocLow);
      var ocHighPoint = getPoint(ocHigh);
      var lowestPoint = getPoint(lowestVal);
      var highestPoint = getPoint(highestVal);
      var whiskerEnds = [[subPixelOptimizePoint(highestPoint), subPixelOptimizePoint(ocHighPoint)], [subPixelOptimizePoint(lowestPoint), subPixelOptimizePoint(ocLowPoint)]];
      var bodyEnds = [];
      addBodyEnd(ocHighPoint, 0);
      addBodyEnd(ocLowPoint, 1);
      var sign;

      if (openVal > closeVal) {
        sign = -1;
      } else if (openVal < closeVal) {
        sign = 1;
      } else {
        // If close === open, compare with close of last record
        if (dataIndex > 0) {
          sign = data.getItemModel(dataIndex - 1).get()[2] <= closeVal ? 1 : -1;
        } else {
          // No record of previous, set to be positive
          sign = 1;
        }
      }

      data.setItemLayout(idx, {
        chartLayout: chartLayout,
        sign: sign,
        initBaseline: openVal > closeVal ? ocHighPoint[constDim] : ocLowPoint[constDim],
        // open point.
        bodyEnds: bodyEnds,
        whiskerEnds: whiskerEnds,
        brushRect: makeBrushRect()
      });
      ++dataIndex;

      function getPoint(val) {
        var p = [];
        p[variableDim] = axisDimVal;
        p[constDim] = val;
        return isNaN(axisDimVal) || isNaN(val) ? [NaN, NaN] : coordSys.dataToPoint(p);
      }

      function addBodyEnd(point, start) {
        var point1 = point.slice();
        var point2 = point.slice();
        point1[variableDim] = subPixelOptimize(point1[variableDim] + candleWidth / 2, 1, false);
        point2[variableDim] = subPixelOptimize(point2[variableDim] - candleWidth / 2, 1, true);
        start ? bodyEnds.push(point1, point2) : bodyEnds.push(point2, point1);
      }

      function makeBrushRect() {
        var pmin = getPoint(Math.min(openVal, closeVal, lowestVal, highestVal));
        var pmax = getPoint(Math.max(openVal, closeVal, lowestVal, highestVal));
        pmin[variableDim] -= candleWidth / 2;
        pmax[variableDim] -= candleWidth / 2;
        return {
          x: pmin[0],
          y: pmin[1],
          width: constDim ? candleWidth : pmax[0] - pmin[0],
          height: constDim ? pmax[1] - pmin[1] : candleWidth
        };
      }

      function subPixelOptimizePoint(point) {
        point[variableDim] = subPixelOptimize(point[variableDim], 1);
        return point;
      }
    });
  });
}

function calculateCandleWidth(seriesModel, data) {
  var baseAxis = seriesModel.getBaseAxis();
  var extent;
  var bandWidth = baseAxis.type === 'category' ? baseAxis.getBandWidth() : (extent = baseAxis.getExtent(), Math.abs(extent[1] - extent[0]) / data.count());
  var barMaxWidth = parsePercent(retrieve2(seriesModel.get('barMaxWidth'), bandWidth), bandWidth);
  var barMinWidth = parsePercent(retrieve2(seriesModel.get('barMinWidth'), 1), bandWidth);
  var barWidth = seriesModel.get('barWidth');
  return barWidth != null ? parsePercent(barWidth, bandWidth) // Put max outer to ensure bar visible in spite of overlap.
  : Math.max(Math.min(bandWidth / 2, barMaxWidth), barMinWidth);
}

module.exports = _default;